package cflow;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;

import logical.Automata;
import logical.AutomataGenerator;
import logical.DFA;
import logical.SetState;
import logical.SimpleNode;
import logical.TransitionDFA;

public class Cflow {

	public static int HALT = 0;
	public static int CONTINUE = 1;
	private static boolean running = true;
	private static int mode = 1;
	private static int delayTime = 1000;
	private static DFA dfa;
	private static SetState currentState;

	private static LinkedList<String> inputTrace = new LinkedList<String>();
	private static LinkedList<String> acceptedTrace = new LinkedList<String>();

	
	public static LinkedList<String> getAcceptedTrace()
	{
		return acceptedTrace;
	}
	public static void main(String args[]) {
		System.out.println("Reading from standard input...");
		System.out.print("Enter a regular expression :");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String s;
		try {
			if ((s = in.readLine()) != null && s.length() != 0)
				new Cflow(s);
		} catch (IOException e) {
			System.out.println("Invalid regex supplied.");
			System.exit(1);
		}
	}

	public Cflow(String regex) {
		regex=regex+'\n';
		init(regex);
	}

	public Cflow(String regex, int mode) {
		regex=regex+'\n';
		Cflow.mode = mode;
		init(regex);
	}

	public Cflow(String regex, int mode, int delay) {
		regex=regex+'\n';
		Cflow.mode = mode;
		delayTime = delay;
		init(regex);
	}

	private void init(String regex) {
		String name = new String(regex);
		InputStream input = new ByteArrayInputStream(regex.getBytes());
		try {
			new AutomataGenerator(input);
			try {
				SimpleNode n = AutomataGenerator.Start();
				Automata automata = n.createAutomata("");
				dfa = new DFA(automata, this);
			
				dfa.createVisualization();
				dfa.setName(name);
				// System.out.println("Thank you.");
			} catch (Exception e) {
				System.err
						.println("Invalid expression detected. Received the following expression:\n"
								+ name
								+ "\nCflow will ignore all further actions.");
				running = false;
				return;
			}
			setCurrentState(dfa.getInitialState());

			Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
				public void run() {
					System.out.println("Execution stopped.\n");
					Cflow.printlog();
				}
			}));
		} catch (Error e) {
			running = false;
			System.out.println("Invalid regex supplied.");
		}

	}

	public static void printlog() {
		String s = "";
		if (getCurrentState().isFinal()) {
			s += "Reached a final state.\n";
		} else {
			s += "Failed to reach a final state.\n";
		}
		if (!inputTrace.isEmpty()) {
			s += "Received the following input sequence:\n";
			for (String value : inputTrace) {
				s += value + "->";
			}
			s = s.substring(0, s.length() - 2);
			System.out.println(s);
		}
		if (!acceptedTrace.isEmpty()) {
			s = "Accepted the following input sequence:\n";
			for (String values : acceptedTrace) {
				s += values + "->";
			}
			s = s.substring(0, s.length() - 2);
			System.out.println(s);
		}
	}

	static public void addTransition(String input) {
		if (!running) {
			return;
		}
		inputTrace.add(input);
		if (!transition(input)) {
			Exception e = new Exception();
			System.err.println("Invalid transition detected");
			System.err.println("Faulty transition called in "
					+ e.getStackTrace()[1].toString()
					+ "\nWill continue recording transitions.");
			return;
		} else {
			acceptedTrace.add(input);
		}
	}

	private static boolean transition(String input) {
		for (TransitionDFA df : getCurrentState().getTransitions()) {
			if (df.getValue().equals(input)) {
				setCurrentState(df.getDestinationState());
				dfa.refresh(getCurrentState());
				if (mode == HALT) {
					try {
						Thread.sleep(delayTime);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				return true;
			}
		}
		return false;
	}

	public static SetState getCurrentState() {
		return currentState;
	}

	public static void setCurrentState(SetState currentState) {
		Cflow.currentState = currentState;
	}
}
